use std::fmt::Debug;
use std::path::Path;


use super::{RecipeResult, RecipeError};
use super::con::{ConRecipeWriter,ConRecipeReader};
use super::file::{FileRecipeWriter,FileRecipeReader};
use super::segment::{SegRecipeWriter,SegRecipeReader};

use super::pointer::ChunkPointer;

use super::file::FileInfo;

use super::container::ContainerId;

use crate::constants::{
	FILE_INFO_RECIPE_NAME,
	SEGMENT_RECIPE_NAME,
	CONTAINER_RECIPE_NAME,
};

pub struct RecipeWriter<T:ChunkPointer+Debug> {
	recipe_dir:String,
	last_con:Option<ContainerId>,
	file:FileRecipeWriter,
	seg:SegRecipeWriter<T>,
	con:ConRecipeWriter,
}

impl<T:ChunkPointer+Debug> RecipeWriter<T> {
	pub fn from_path<P: AsRef<Path>+?Sized>(path:&P) -> RecipeResult<Self> {
		std::fs::create_dir(path).map_err(|e|RecipeError::RecipeWriterOpen(e))?;
		let path = path.as_ref();
		Ok(Self{
			recipe_dir:path.to_str().unwrap().to_string(),
			last_con:None,
			file:FileRecipeWriter::from_path(&path.join(FILE_INFO_RECIPE_NAME))?,
			seg:SegRecipeWriter::from_path(&path.join(SEGMENT_RECIPE_NAME))?,
			con:ConRecipeWriter::from_path(&path.join(CONTAINER_RECIPE_NAME))?,
		})
	}
	

	
	pub fn dir(&self) -> &String {
		&self.recipe_dir
	}

	pub fn append_file(&mut self,f:&FileInfo) -> RecipeResult<()> {
		self.file.append(f)?;
		Ok(())
	}

	pub fn append_chunk_pointer(&mut self,t:&T) -> RecipeResult<()> {
		self.seg.append(t)?;
		let id = t.containerid();
		if let Some(last_con) = self.last_con {
			if last_con == id {
				return Ok(())
			}
		}
		self.last_con = Some(id);
		self.con.append(id)?;
		Ok(())
	}
}

pub struct RecipeReader<T:ChunkPointer+Debug> {
	recipe_dir:String,
	file:FileRecipeReader,
	seg:SegRecipeReader<T>,
	con:ConRecipeReader,
}

impl<T:ChunkPointer+Debug> RecipeReader<T> {
	pub fn from_path<P: AsRef<Path>+?Sized>(path:&P) -> RecipeResult<Self> {
		let path = path.as_ref();
		assert!(path.metadata().map_err(|e|RecipeError::RecipeReaderOpen(e))?.is_dir());
		Ok(Self{
			recipe_dir:path.to_str().unwrap().to_string(),
			file:FileRecipeReader::from_path(&path.join(FILE_INFO_RECIPE_NAME))?,
			seg:SegRecipeReader::from_path(&path.join(SEGMENT_RECIPE_NAME))?,
			con:ConRecipeReader::from_path(&path.join(CONTAINER_RECIPE_NAME))?,
		})
	}
	
	pub fn dir(&self) -> &String {
		&self.recipe_dir
	}

	pub fn next_file(&mut self) -> RecipeResult<Option<Box<FileInfo>>> {
		self.file.get()
	}

	pub fn next_chunk_pointer(&mut self) -> RecipeResult<Option<Box<T>>> {
		self.seg.get()
	}

	pub fn next_con(&mut self) -> RecipeResult<Option<ContainerId>> {
		self.con.get()
	}
}
